package qc.module.iot.protocol.ddp;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qc.common.core.enums.iot.IotPacketsDataFrameTypeEnum;
import qc.common.core.utils.RadixConvertUtil;
import qc.module.iot.protocol.IotFrameParseBase;
import qc.module.iot.protocol.IotFrameParseResult;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

/**
 * 宏电DTU DDP_V30数据帧解析类
 *
 * @author QuCheng Tech
 * @since 2025/11/9
 */
public class DDP_V30_FrameParse extends IotFrameParseBase {
    private static final Logger logger = LoggerFactory.getLogger(DDP_V30_FrameParse.class);

    //宏电DTU DDP协议规范 V30参考：https://max.book118.com/html/2017/0523/108558670.shtm

    /**
     * 解析数据帧错误提示信息前缀
     */
    private static final String parseErrorMessagePrefix = "使用宏电DTU  DDP V30协议解析数据帧时错误，";

    /**
     * 数据帧最小长度，包含起始符和结束符的最小有效长度
     */
    private static final int Frame_Length_Min_Total = 16;
    //数据帧Hex
    //7E7E015308290013A00032002B020016250515204517F1F1530829001348F0F025051520003923000059492019000000261903150538121274034408

    //每种数据协议中使用静态定义协议中的数据帧起始符、结束符等，通过构造函数传入到父类中
    /**
     * 数据帧起始符字节数组
     */
    private static final byte Frame_Start_Byte = 0x7B;
    /**
     * 数据帧结束符字节数组
     */
    private static final byte Frame_End_Byte = 0x7B;

    public DDP_V30_FrameParse() {
        //通过调用父类的构造函数传入协议中的固定内容
        super(null, new byte[]{Frame_Start_Byte}, new byte[]{Frame_End_Byte}, Frame_Length_Min_Total);
        //也可以在调用构造函数后显式对变量或调用方法进行赋值
    }

    //如果只根据起始字符和结束字符进行判断，可以直接使用父类中提供的通用方法（不要重写父类方法），或者在重写父类方法中调用父类提供的方法
    //@Override
    //public List<IotPacketsSplitResult> split(byte[] packetsBytes) {
    //    logger.debug("split");
    //    
    //    //return super.split(bytes);
    //
    //    return null;
    //}

    /**
     * 解析数据帧
     *
     * @param inputBytes    数据帧报文字节数组，数组起始必须时数据值对应协议的起始符
     * @param parseBodyData 是否解析报文中的正文内容数据，为false时表示只解析数据帧基本结构框架，为true时表示解析解析结构框架和正文内容数据
     * @return qc.module.iot.protocol.IotFrameParseResult
     * @author QuCheng Tech
     * @since 2025/11/5
     */
    @Override
    public IotFrameParseResult parse(byte[] inputBytes, boolean parseBodyData) {
        logger.debug("parseBaseStruct");

        //初始化解析返回结果，设置解析成功标记为false
        IotFrameParseResult result = new IotFrameParseResult();
        result.setParseSuccess(false);
        result.setDataTime(null);//初始化数据时标为null
        //输入的字节数组赋值，根据数据协议确定是否要对Hex字符串和Ascii字符串进行赋值
        result.setInputBytes(inputBytes);
        result.setInputHexString(RadixConvertUtil.toHexString(inputBytes));

        /* 宏电DTU DDP_V30 报文数据帧
         * 结构框架：起始标志 包类型 包长度 DTU身份识别码 数据 结束标志
         * 长度字节：   1     1     2      11        n    1
         * 固定内容： 起始标志-7B、结束标志-7B
         * 解析举例：7B 01 0016 3135313236393936393734 0A0E88190FA2 7B
         * 解析举例：7B 01 0016 3030303030303030313300 0A6C48370FA2 7B
         */
        if (inputBytes == null || inputBytes.length < this.frameMinLength)
            return returnParseError(result, "报文内容为空");
        //记录当前解析判断到的位置索引，数据帧的每个部分在解析前均要判断输入的字节数组总长是否足够
        int index = 0x0;
        //判断有效数据帧报文最小总长度
        int frameMinTotalLength = 0x0;
        //数据帧的每个结构框架部分的长度，判断到每个部分长度时进行赋值
        int framePartLength = 0x0;

        //起始标志，长度为1字节
        //framePartLength=this.frameStartBytes.length;//可以从静态定义中读取长度，也可以直接固定长度
        framePartLength = 0x1;
        frameMinTotalLength = index + framePartLength;
        if (inputBytes.length < frameMinTotalLength)
            return returnParseError(result, "判断帧起始标志时报文长度不足，报文总长度应至少为" + frameMinTotalLength);
        for (int i = 0; i < framePartLength; i++) {
            if (inputBytes[index + i] != this.frameStartBytes[i])
                return returnParseError(result, "帧起始标志错误");
        }
        index += framePartLength;//帧起始标志判断通过，索引加上帧起始标志长度

        //包类型，长度为1字节
        framePartLength = 0x1;
        frameMinTotalLength = index + framePartLength;
        if (inputBytes.length < frameMinTotalLength)
            return returnParseError(result, "判断帧包类型时报文长度不足，报文总长度应至少为" + frameMinTotalLength);
        byte packetsTypeByte = inputBytes[index];
        byte[] packetsTypeBytes = Arrays.copyOfRange(inputBytes, index, index + framePartLength);
        //解析结果对象中使用功能码属性存储包类型
        result.setFunctionCodeBytes(packetsTypeBytes);
        result.setFunctionCode(RadixConvertUtil.toHexString(packetsTypeBytes));
        index += framePartLength;//帧功能码判断通过，索引加上帧功能码长度
        //包类型解析，根据包类型对数据值类型进行赋值，确定是注册、心跳还是数据帧
        DDP_V30_DtuSendPacketsTypeEnum functionCodeEnum = DDP_V30_DtuSendPacketsTypeEnum.getDDP_V30_DtuSendPacketsTypeEnum(packetsTypeByte);
        result.setFrameName(functionCodeEnum.getName());
        IotPacketsDataFrameTypeEnum frameTypeEnum = IotPacketsDataFrameTypeEnum.UN_DEFINE;
        if (functionCodeEnum == DDP_V30_DtuSendPacketsTypeEnum.SEND_USER_DATA)
            frameTypeEnum = IotPacketsDataFrameTypeEnum.DATA;
        result.setFrameType(frameTypeEnum);

        //包长度，长度为2字节
        framePartLength = 0x2;
        frameMinTotalLength = index + framePartLength;
        if (inputBytes.length < frameMinTotalLength)
            return returnParseError(result, "判断帧包长度时报文长度不足，报文总长度应至少为" + frameMinTotalLength);
        byte[] directionFlagAndLengthBytes = Arrays.copyOfRange(inputBytes, index, index + framePartLength);
        index += framePartLength;//帧包长度判断通过，索引加上帧包长度的长度
        //计算包长度，高位在前、低位在后
        int contentLength = RadixConvertUtil.computeIntValue(directionFlagAndLengthBytes[0x0], directionFlagAndLengthBytes[0x1]);
        result.setBodyLength(contentLength);
        //根据内容长度判断报文长度是否足够
        if (contentLength > 0x0) {
            logger.debug("报文中内容正文长度解析为：" + contentLength);
            //DDP协议中长度是指一条完整报文的总长度，包含起始标志至结束标志的总长度；设置数据帧的最小长度=解析出来的长度值
            frameMinTotalLength = contentLength;
            if (inputBytes.length < frameMinTotalLength)
                return returnParseError(result, "根据报文中的正文长度[" + contentLength + "]判断报文长度不足，报文总长度应至少为" + frameMinTotalLength);

            //获取数据帧包长度后的字节数组，依次为：DTU身份识别码(11字节)、数据(n字节)、结束标志(1字节)
            byte[] packetsRemainBytes = Arrays.copyOfRange(inputBytes, index, contentLength);
            //判断结束标志是否正确
            if (packetsRemainBytes[packetsRemainBytes.length - 0x1] != Frame_End_Byte)
                return returnParseError(result, "帧结束标志错误");

            //前面11字节为DTU身份识别码，使用遥测站地址属性进行存储
            byte[] dtuIndentityBytes = Arrays.copyOfRange(packetsRemainBytes, 0x0, 11);
            result.setStationAddressBytes(dtuIndentityBytes);
            result.setStationAddress(RadixConvertUtil.toHexString(dtuIndentityBytes));
            //DTU身份识别码至结束标志中间为数据
            byte[] userDataBytes = Arrays.copyOfRange(packetsRemainBytes, 11, packetsRemainBytes.length - 0x1);//截止位置索引减去结束标记的1字节长度
            String userDataHexString = RadixConvertUtil.toHexString(userDataBytes);
            logger.debug("报文中内容正文HEX为：" + userDataHexString);
            result.setBodyDataBytes(userDataBytes);
            result.setBodyDataString(userDataHexString);

            //DDP协议中长度是指一条完整报文的总长度，设置位置索引=长度            
            index = contentLength;
        }

        //全部判断和验证均通过，解析成功；设置完整的数据帧字节数组
        result.setParseSuccess(true);
        byte[] frameBytes = Arrays.copyOfRange(inputBytes, 0x0, index);
        result.setParseFrameBytes(frameBytes);
        result.setParseFrameString(RadixConvertUtil.toHexString(frameBytes));

        return result;
    }

    /**
     * 返回数据帧解析错误结果
     *
     * @param result   返回解析结果对象
     * @param errorMsg 错误信息，返回结果中自动加上统一的前缀
     * @return qc.module.iot.protocol.IotFrameParseResult
     * @author QuCheng Tech
     * @since 2025/11/7
     */
    IotFrameParseResult returnParseError(IotFrameParseResult result, String errorMsg) {
        if (result == null) {
            result = new IotFrameParseResult();
            result.setParseSuccess(false);
        }

        result.setParseErrorMsg(parseErrorMessagePrefix + errorMsg);

        return result;
    }

    /**
     * 生成回复报文，根据传入的报文解析结果判断，如果返回空表示不需要回复
     *
     * @param frameParseResult 收到的报文解析结果
     * @return byte[]
     * @author QuCheng Tech
     * @since 2025/11/8
     */
    @Override
    public byte[] generateReplyFrame(IotFrameParseResult frameParseResult) {
        logger.debug("generateReplyFrame");
        //如果解析结果为空或者解析错误不需要进行回复
        if (frameParseResult == null) {
            logger.debug("generateReplyFrame 传入的DDP协议帧解析结果为null，无法生成回复报文");
        } else {
            if (frameParseResult.isParseSuccess() == false) {
                logger.debug("generateReplyFrame 传入的DDP协议帧帧解析结果为解析失败，无法生成回复报文");
            } else {
                //DDP协议根据包类型确定是否需要回复及回复报文内容，DDP协议解析结果中使用功能码属性存储包类型
                if (frameParseResult.getFunctionCodeBytes() == null || frameParseResult.getFunctionCodeBytes().length < 0x1) {
                    logger.debug("generateReplyFrame 传入的DDP协议帧解析结果中包类型为空，无法生成回复报文");
                } else {
                    byte packetsTypeByte = frameParseResult.getFunctionCodeBytes()[0x0];
                    DDP_V30_DtuSendPacketsTypeEnum dtuSendPacketsTypeEnum = DDP_V30_DtuSendPacketsTypeEnum.getDDP_V30_DtuSendPacketsTypeEnum(packetsTypeByte);
                    //回复时需要有DTU身份识别码，先进行判断是否有，DDP协议中使用测站地址属性存储DTU身份识别码
                    if (frameParseResult.getStationAddressBytes() == null) {
                        logger.debug("generateReplyFrame 传入的DDP协议帧解析结果中DTU身份识别码为空，无法生成回复报文");
                    } else {
                        /* 宏电DTU DDP_V30 报文数据帧
                         * 结构框架：起始标志 包类型 包长度 DTU身份识别码 数据 结束标志
                         * 长度字节：   1     1     2      11        n    1
                         * 固定内容： 起始标志-7B、结束标志-7B
                         */

                        List<Byte> result = new LinkedList<>();
                        //起始标志
                        result.add(Frame_Start_Byte);
                        //包类型，暂时只对DTU注册、注销、向DSC发送用户数据进行回复
                        if (dtuSendPacketsTypeEnum == DDP_V30_DtuSendPacketsTypeEnum.LOGON_IN) {
                            //收到DTU的注册包，可以回复成功或无效
                            result.add((byte) DDP_V30_DscSendPacketsTypeEnum.LOGON_IN_SUCCESS.getIndex());
                            //result.add((byte) DDP_V30_DscSendPacketsTypeEnum.INVALID.getIndex());
                        } else if (dtuSendPacketsTypeEnum == DDP_V30_DtuSendPacketsTypeEnum.LOGON_OUT) {
                            //收到DTU的注销包，可以回复成功或无效
                            result.add((byte) DDP_V30_DscSendPacketsTypeEnum.LOGON_OUT_SUCCESS.getIndex());
                            //result.add((byte) DDP_V30_DscSendPacketsTypeEnum.INVALID.getIndex());
                        } else if (dtuSendPacketsTypeEnum == DDP_V30_DtuSendPacketsTypeEnum.LOGON_OUT) {
                            //收到DTU发送给DSC的用户数据包
                            result.add((byte) DDP_V30_DscSendPacketsTypeEnum.RECEIVE_USER_DATA.getIndex());
                            //发送用户数据包类型不在此处进行回复，由数据帧解析后生成回复的数据帧再按DDP协议打包后发送回复
                        } else {
                            logger.warn("generateReplyFrame 传入的DDP协议帧解析结果中包类型为[{}]，没有对应包类型的回复报文生成处理，无法生成回复报文", dtuSendPacketsTypeEnum);
                            return null;
                        }
                        //包长度：回复的都没有包含数据，固定长度为16字节
                        //包长度占2个字节，第1字节为0，第2字节为16
                        result.add((byte) 0x0);
                        result.add((byte) 16);
                        //DTU身份识别码，在DDP协议解析结果中使用遥测站地址存储DTU身份识别码
                        for (int i = 0; i < frameParseResult.getStationAddressBytes().length; i++) {
                            result.add(frameParseResult.getStationAddressBytes()[i]);
                        }
                        //结束标志
                        result.add(Frame_End_Byte);

                        //返回生成的回复报文
                        if (result.size() > 0x0) {
                            byte[] bytes = RadixConvertUtil.toByteArray(result);
                            logger.debug("generateReplyFrame 生成的DDP协议回复报文为[" + RadixConvertUtil.toHexString(bytes) + "]");
                            return bytes;
                        }
                    }
                }
            }
        }

        return null;
    }
}
